#pragma once

#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/time.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include <memory>
#include <map>
#include <mutex>
#include <thread>

using namespace std;

enum class TaskType{Socket, Time};

enum class SocketError{Read, Write, Build, Connect, Other};
enum class SocketStatus{Read, Write, Wait};

class Task{
private:
	int _id;
	void* _task;
	TaskType _type;

public:
	Task(void* task, TaskType type):_task(task), _type(type){
		timeval tv;
		gettimeofday(&tv, NULL);
		_id = tv.tv_sec*1000+(tv.tv_usec/1000);
	}

	inline int Get_id() const{
		return _id;
	}

	inline void* Get_task(){
		return _task;
	}

	inline TaskType Get_type(){
		return _type;
	}
};

class TimeCallbackImpl{
public:
	TimeCallbackImpl(int count=-1, double interval = 1.0):_ExecCount(count), _EndTime(0), _time(interval){}
	virtual ~TimeCallbackImpl(){}
	virtual int callback(){}

	inline double Get_Interval() const{
		return _time;
	}

	inline void Set_Interval(double time){
		_time = time;
	} 

	inline bool ResetStart(){
		timeval tv;
		gettimeofday(&tv, NULL);
		_EndTime=_time * 1000.0 + tv.tv_sec*1000.0+(tv.tv_usec/1000.0);
		
		if (_ExecCount == -1)
			return true;
		else
			return --_ExecCount;
	}

	inline bool IsFinish(){
		timeval tv;
		gettimeofday(&tv, NULL);
		return _EndTime <= tv.tv_sec*1000.0+(tv.tv_usec/1000.0);
	}
private:
	double _EndTime;
	double _time;
	int _ExecCount;
};

class SocketImpl{
public:
    SocketImpl():_IsServer(true){}
	virtual ~SocketImpl(){}
	virtual int Recv(){} //recv finish return 0, recving return 1, error return -1
	virtual int Send(){} //send finish return 0, sending return 1, error return -1
	virtual SocketImpl* Accept(int&){}
	virtual int Error(const SocketError&){}
	
	inline int GetSocket() const{
		return _socket;	
	}

	inline int SetSocket(const int& sock){
		_socket = sock;
	}
	
	inline void open_server_mode(){
	    _IsServer = true;
	}
	
	inline void open_client_mode(){
	    _IsServer = false;
	}
	
	inline int BuildConnection(const string& ip = "127.0.0.1", const int& port = 12345){
	    if(_IsServer)
			return create_server(ip, port);
	    else
			return create_client(ip, port);
	}
	
	inline SocketStatus Get_status(){
	    return _status;
	}
	inline void Set_status(const SocketStatus& s){
	    _status = s;
	}

	inline bool IsServer(){
		return _IsServer;
	}
	
	inline int create_server(const string& ip, const int& port){
		_socket = socket(PF_INET, SOCK_STREAM, 0);
        fcntl(_socket, F_SETFL, fcntl(_socket, F_GETFD, 0)|O_NONBLOCK);
		
		struct sockaddr_in servaddr;
		memset(&servaddr, 0, sizeof(servaddr));
		servaddr.sin_family = AF_INET;
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		servaddr.sin_port = htons(port);

		bind(_socket, (struct sockaddr *)&servaddr, sizeof(servaddr));
		listen(_socket, 20);
		return _socket;
	}

	inline int create_client(const string& ip, const int& port){
		struct sockaddr_in addr;  
		addr.sin_family = AF_INET;  
		addr.sin_port = htons(port);  
		addr.sin_addr.s_addr = inet_addr(ip.c_str());
		memset(&addr.sin_zero, 0, 8);

		_socket = socket(PF_INET,SOCK_STREAM,0);
		fcntl(_socket, F_SETFL, fcntl(_socket, F_GETFD, 0)|O_NONBLOCK);
		connect(_socket,(struct sockaddr*)&addr,sizeof(addr));
		return _socket;
	}	

protected:
    bool _IsServer;
	int _socket;
	SocketStatus _status;
};

class AutoCallbackChain{
public:
    AutoCallbackChain();
	inline int append(TimeCallbackImpl* pTime){
		lock_guard<mutex> lock(_mutex);
		Task task((void*)pTime, TaskType::Time);
		_mapTasks.insert(pair<int, Task>(task.Get_id(), task));
		return task.Get_id();
	}
	
	inline int append(SocketImpl*& pSock){
	    lock_guard<mutex> lock(_mutex);
		if(pSock->IsServer())
			_mapServerFDs.insert(pair<int, SocketImpl*>(pSock->GetSocket(), pSock));	
	    epoll_event ev;  
        ev.events = EPOLLIN | EPOLLET;  
        ev.data.fd = pSock->GetSocket();     
        fcntl(pSock->GetSocket(), F_SETFL, fcntl(pSock->GetSocket(), F_GETFD, 0)|O_NONBLOCK);
        epoll_ctl(_iEpollFd, EPOLL_CTL_ADD, pSock->GetSocket(), &ev);  
        _mapSocketImpls.insert(pair<int, SocketImpl*>(pSock->GetSocket(), pSock));
        return pSock->GetSocket();
	}
	
	inline int remove(const int& id){
		lock_guard<mutex> lock(_mutex);
		return _mapTasks.erase(id);	
	}
	
	int Run();
	void listen();
private:
	map<int, Task> _mapTasks;	
	
	map<int, SocketImpl*> _mapSocketImpls;			
	map<int, SocketImpl*> _mapServerFDs;			//key:server fd, value: server is live?
	int _iEpollFd;
	mutex _mutex;
};
